package com.reger.core.logging;

import java.lang.reflect.Method;
import java.util.Map;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.web.bind.annotation.ResponseBody;

import com.reger.core.Context;
import com.reger.core.init.ControllerBefore;
import com.reger.core.init.ControllerReturning;
import com.reger.core.init.ControllerThrowing;
import com.reger.core.utils.ClasseUtils;

/**
 * Aop 访问日志
 * 
 * @author lenovo
 *
 */
@Aspect
class ControllerLogAop implements ApplicationContextAware{
	
	private static final Logger log = LoggerFactory.getLogger(ControllerLogAop.class);
	
	private ApplicationContext applicationContext;
	private volatile Map<String, ControllerBefore> beforeMaps;
	private volatile Map<String, ControllerReturning> returningMaps;
	private volatile Map<String, ControllerThrowing> throwingMaps;

	public ControllerLogAop() {
		log.debug("开始初始化控制器aop...");;
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext=applicationContext;
	}
	
	@Around("within(@org.springframework.web.bind.annotation.RestController *)")
	public Object restRecordSysLog(ProceedingJoinPoint point) throws Throwable {
		Method method = ((MethodSignature) point.getSignature()).getMethod();
		Context.isRest(true);
		return recordSysLog(point, method);
	}

	@Around("within(@org.springframework.stereotype.Controller *)")
	public Object otherRecordSysLog(ProceedingJoinPoint point) throws Throwable {
		Method method = ((MethodSignature) point.getSignature()).getMethod();
		Context.isRest(ClasseUtils.hasAnnotation(method, ResponseBody.class));
		return recordSysLog(point, method);
	}
	
	
	public Object recordSysLog(ProceedingJoinPoint point, Method method) throws Throwable {
		Context.put("ControllerMethod",method);
		log.debug("进入控制器aop方法");
		boolean now = Context.now();
		String methodName = ClasseUtils.getMethodName(method);
		Object params = getParamObject(point.getArgs());
		RunTimeLog.log("方法执行开始:{}", methodName);
		
		try {
			this.before(point, method);
			Object relust = point.proceed();
			this.returning(point, relust, method);
			return relust;
		}catch (Throwable throwable) {
			this.throwing(point, throwable, method);
			throw throwable;
		}finally {
			RunTimeLog.log("方法执行结束:{} ,[参数]:{}", methodName, params);
			if(now)Context.remove();
		}
	}

	
	private void before(ProceedingJoinPoint point, Method method) {
		if(beforeMaps==null)
			beforeMaps = applicationContext.getBeansOfType(ControllerBefore.class);
		beforeMaps.values().forEach(cp->cp.apply(point, method));
	}
	
	private void returning(ProceedingJoinPoint point,Object relust, Method method) {
		if(returningMaps==null)
			returningMaps = applicationContext.getBeansOfType(ControllerReturning.class);
		returningMaps.values().forEach(cp->cp.apply(point, relust, method));
	}
	
	private void throwing(ProceedingJoinPoint point,Throwable throwable, Method method) {
		if(throwingMaps==null)
			throwingMaps = applicationContext.getBeansOfType(ControllerThrowing.class);
		throwingMaps.values().forEach(cp->cp.apply(point, throwable, method));
	}
	
	private Object getParamObject(Object[] param) {
		return new Object(){
			@Override
			public String toString() {
				StringBuffer stringBuffer=new StringBuffer();
				for (Object object : param) {
					stringBuffer.append(" , ");
					stringBuffer.append(String.valueOf(object));
				}
				return stringBuffer.toString();
			}
		};
	}
}
